/**************************************************************************************************************
 * @copyright Copyright (c) 2015—2024 Uniorange Internet Design Co., LTD. All rights reserved.
 * @author Uniorange Internet Design Co., LTD.
 *
 * This file is part of the App project. Copyright © Uniorange Internet Design Co., Ltd. All Rights Reserved.
 * Unauthorized copying of this file, via any medium is strictly prohibited Proprietary and confidential.
 *
 **************************************************************************************************************/
package com.jinyu.servicedemo.handler;

import com.jinyu.core.protocol.*;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author 邱金波
 * @version V0.1
 * @className SimpleClientHandler
 * @description 客户端handler
 * @date 2024/09/14 16:05
 */
@ChannelHandler.Sharable
@Component
@Slf4j
@Data
public class ClientDispatchHandler extends SimpleChannelInboundHandler<Header> {
    private static ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);

    @Value("${service.name}")
    private String name;
    @Value("${service.host}")
    private String host;

    @Value("subscribeName")
    private String subscribeName;

    @Value("${service.port}")
    private int port;

    private Bootstrap bootstrap;

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //连接成功之后，向服务端注册服务信息
        RegisterProtocol registerProtocol = new RegisterProtocol(host, port, name);
        registerProtocol.setType(ProtocolTypeEnum.REGISTER.getType());
        registerProtocol.setVersion(1);
        ctx.writeAndFlush(registerProtocol);
        //定期从注册中心拉注册服务信息
        executorService.scheduleAtFixedRate(() -> {
            GetSubscribeList getSubscribeList = new GetSubscribeList();
            getSubscribeList.setData(name);
            ctx.writeAndFlush(getSubscribeList);
        }, 1000, 3000, TimeUnit.MILLISECONDS);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("channel inactive:{}", ctx.channel().remoteAddress());
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        log.info("channel unregistered:{}", ctx.channel().remoteAddress());
        if (bootstrap != null) {
            reconnect(ctx);
        }
    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Header header) throws Exception {
        if (header instanceof PongProtocol) {
            log.info("PONG PROTOCOL RECEIVED");
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            switch (((IdleStateEvent) evt).state()) {
                case READER_IDLE:
                    break;
                case WRITER_IDLE:
                    break;
                case ALL_IDLE: {
                    log.info("PING PROTOCOL SENDED:{}", ctx.channel().id());
                    PingProtocol pingProtocol = new PingProtocol();
                    pingProtocol.setType(ProtocolTypeEnum.PING.getType());
                    pingProtocol.setVersion(1);
                    ctx.writeAndFlush(pingProtocol);
                }
                break;
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("exception caught:{}", cause);
        ctx.close();
    }

    private void reconnect(ChannelHandlerContext ctx) {
        ctx.executor().schedule(() -> {
            bootstrap.connect(ctx.channel().remoteAddress());
        }, 3, TimeUnit.SECONDS);
    }
}
